#include <memory>
#include <vector>
#include <unistd.h>
#include "thread.hpp"
#include "mutex.hpp"
#include "threadPool.hpp"
#include "ProConModel.hpp"
#include "task.hpp"

using namespace std;

int tickets = 100000;
pthread_mutex_t mutex;
pthread_cond_t cond;

// 测试多线程下，对同一个锁进行初始化销毁是否存在问题
// 发现确实有问题
void test(void *args)
{
    usleep(1);
    pthread_mutex_init(&mutex, nullptr);
    pthread_mutex_destroy(&mutex);
}

void *BuyTickets(void *args)
{
    string name = *static_cast<string *>(args);
    while (true)
    {

        {
            LockGuard lk(&mutex);
            if (tickets > 0)
                tickets--;
            else
                break;
        }
    }
    cout << name << " 买票成功" << tickets << endl;
}

// 线程+互斥量
void test_buyticket()
{
    int num = 10;
    vector<shared_ptr<string>> vsp;
    vector<Thread> vtd;
    for (int i = 0; i < num; i++)
    {
        char buf[32];
        snprintf(buf, sizeof(buf), "线程-%d", i + 1);
        vsp.push_back(make_shared<string>(buf));
        vtd.emplace_back(BuyTickets, i + 1, vsp[i].get());
    }

    for (int i = 0; i < num; i++)
    {
        vtd[i].join();
    }
}

// 当抢票到0我们直接退出break，不合理的
// 系统上，如果抢完票后，还存在一个放票的环节，此时线程是不能退出的
// 只能循环的去抢票，等票
// 但有个问题，如果break直接取消，会导致线程频繁的申请锁，释放锁
// 可能回导致线程饥饿问题，让其他线程申请不到锁
// 为了让线程之间有一定的顺序性，总不能让一个线程霸占锁，我们使用
// 条件变量，让线程之间具有一定的同步关系

// 条件变量：pthread_cond_t cond;
// 类似于一个结构体，其内部有个阻塞队列，当某个条件不满足时，释放锁，去改阻塞队列中等待
// 等到条件满足后，线程被唤醒，重新去申请锁,这些动作都是在函数内部进行完成的。
// 即pthread_cond_wait函数: 1.释放锁，2.到阻塞队列等待  3.被唤醒 4.申请锁，申请成功向下继续执行

void *BuyTickets2(void *args)
{
    string name = *static_cast<string *>(args);
    while (true)
    {
        pthread_mutex_lock(&mutex);
        // 循环等待防止误唤醒
        while (tickets <= 0)
        {
            cout << name << "在cond下等待" << endl;
            pthread_cond_wait(&cond, &mutex);
        }

        // 执行到这里一定是有票的
        cout << name << " 买票成功，" << tickets-- << endl;
        pthread_mutex_unlock(&mutex);
    }
    cout << name << " 买票成功" << tickets << endl;
}

// 线程+互斥量+条件变量
void test_buyticket2()
{
    int num = 6;
    vector<shared_ptr<string>> vsp;
    vector<Thread> vtd;
    for (int i = 0; i < num; i++)
    {
        char buf[32];
        snprintf(buf, sizeof(buf), "线程-%d", i + 1);
        vsp.push_back(make_shared<string>(buf));
        vtd.emplace_back(BuyTickets2, i + 1, vsp[i].get());
    }
    // 模拟放票环节
    int cnt = 0;
    while (true)
    {
        if (tickets == 0)
        {
            cout << "我是主线程，我准备放票了..." << endl;
            sleep(2);
            tickets = 100000;
            // 唤醒等待的线程
            // 唤醒该条件变量下的所有线程
            pthread_cond_broadcast(&cond);
            cout << "放票成功" << endl;
            cnt++;
        }
        if (cnt == 3)
            exit(1);
    }

    for (int i = 0; i < num; i++)
    {
        vtd[i].join();
    }
}

void test_pro_con()
{
    ProConModel<int> pc(BUFF_TYPE::DOUBLE);
    // 绑定同一个对象，使生产者和消费者看到同一份缓冲区
    Thread consumer(std::bind(&ProConModel<int>::consumer, pc), "消费者", nullptr);
    Thread producer(std::bind(&ProConModel<int>::producer, pc), "生产者", nullptr);

    consumer.join();
    producer.join();
}

// 处理任务
void excuteTask(Task &t)
{
    switch (t._op)
    {
    case '+':
        t._res = t._x + t._y;
        break;
    case '-':
        t._res = t._x - t._y;

        break;
    case '*':
        t._res = t._x * t._y;

        break;
    case '/':
        if(t._y == 0)
        {
            t._errno = 1;
            t._err_str = "除0错误";
            break;
        }            
        t._res = t._x / t._y;
        break;
    case '%':
        if(t._y == 0)
        {
            t._errno = 1;
            t._err_str = "除0错误";
            break;
        }            
        t._res = t._x % t._y;
        break;
    default:
        t._errno = 2;
        t._err_str = "未定义运算符";
        break;
    }
}
// 产生任务
void produceTask(Task *t)
{
    string op = "+-*/";
    t->_x = rand() % 100;
    t->_y = rand() % 100;
    t->_op = op[(rand() % 4)];
}

void test_thread_pool()
{
    ThreadPool<Task>* ptrl = ThreadPool<Task>::GetInstance(excuteTask,produceTask,3);
    while(true)
    {
        sleep(1);
    }
    // ptrl->producer();
}

int main()
{

    // std::shared_ptr<std::string> sp = std::make_shared<std::string>("线程-1");
    // std::shared_ptr<std::string> sp(new std::string("线程-1"));
    pthread_mutex_init(&mutex, nullptr);
    pthread_cond_init(&cond, nullptr);
    test_thread_pool();

    pthread_mutex_destroy(&mutex);
    pthread_cond_destroy(&cond);
    return 0;
}